#![allow(unused_imports)]

//! Common codec framework for encoding and decoding byte types.
//!
//! This is designed to mimic the very sensible decisions already made by the
//! IETF MLS standard available at `https://datatracker.ietf.org/doc/rfc9420/`
//! but made more general for our use cases.
//!
//! The trait hierarchy is also inspired by `serde`'s types, but oriented more
//! specifically around byte serialization.

use std::io;
use std::mem;
use std::ptr;
use std::str;

pub use crate::errors::CodecError;
use crate::varint::{self, Varint};

/// Collects slices of bytes being progressively appended to a buffer.
pub trait Encoder {
    /// Pushes a buffer onto the output.
    fn push_next(&mut self, buf: &[u8]) -> Result<(), CodecError>;
}

impl Encoder for Vec<u8> {
    fn push_next(&mut self, buf: &[u8]) -> Result<(), CodecError> {
        self.extend_from_slice(buf);
        Ok(())
    }
}

/// Shim to use an instance of an encoder as a [``io::Write``].
pub struct EncWriter<'e, E: ?Sized> {
    enc: &'e mut E,
}

impl<'e, E: Encoder> io::Write for EncWriter<'e, E> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // TODO fix types to avoid needing expect, `push_next` probably
        // shouldn't be able to fail
        self.enc.push_next(buf).expect("codec: write wrapper");
        Ok(buf.len())
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/// Extension trait to encode more common structures.
pub trait EncoderExt {
    /// Pushes a byte.
    fn push_u8(&mut self, v: u8) -> Result<(), CodecError>;

    /// Pushes a varint.
    fn push_varint(&mut self, vi: Varint) -> Result<(), CodecError>;

    /// Pushes a varint-sized buf
    fn push_var_buf(&mut self, buf: &[u8]) -> Result<(), CodecError>;

    /// Wraps this encoder as an `EncWriter` instance.
    fn as_write<'e>(&'e mut self) -> EncWriter<'e, Self>;
}

impl<T: Encoder> EncoderExt for T {
    fn push_u8(&mut self, v: u8) -> Result<(), CodecError> {
        self.push_next(::std::slice::from_ref(&v))
    }

    fn push_varint(&mut self, vi: Varint) -> Result<(), CodecError> {
        self.push_next(vi.as_slice())
    }

    fn push_var_buf(&mut self, buf: &[u8]) -> Result<(), CodecError> {
        // Repeat this check early, since bufs might be >4 GiB.
        if buf.len() > varint::MAX as usize {
            return Err(CodecError::InvalidVarintVal(buf.len() as u64));
        }

        self.push_varint(Varint::new(buf.len() as u32)?)?;
        self.push_next(buf)?;
        Ok(())
    }

    fn as_write<'e>(&'e mut self) -> EncWriter<'e, Self> {
        EncWriter { enc: self }
    }
}

/// Provides sequential slices of bytes from a buffer.
pub trait Decoder<'de> {
    /// Takes some chunk of bytes from the source buffer.  Implementations of
    /// this function MUST return EXACTLY the number of bytes requested or an
    /// error.  Consumers are not expected to validate the size of the returned
    /// buffer.  I should make this be a type parameter or something.
    fn take_next(&mut self, n: usize) -> Result<&'de [u8], CodecError>;
}

pub struct BufDecoder<'b> {
    buf: &'b [u8],
    at: usize,
}

impl<'b> BufDecoder<'b> {
    pub fn new(buf: &'b [u8]) -> Self {
        Self { buf, at: 0 }
    }

    pub fn finished(&self) -> bool {
        self.at == self.buf.len()
    }

    pub fn bytes_left(&self) -> usize {
        self.buf.len() - self.at
    }

    /// If the pointer can be advanced by the specified number of bytes,
    /// advances it that many bytes.
    ///
    /// Returns if it advanced or not.
    pub fn advance(&mut self, n: usize) -> bool {
        if self.at + n > self.buf.len() {
            false
        } else {
            self.at += n;
            true
        }
    }

    /// Returns the remaining bytes in the buffer.
    pub fn remaining_buf(&self) -> &[u8] {
        &self.buf[self.at..]
    }
}

impl<'b> Decoder<'b> for BufDecoder<'b> {
    fn take_next(&mut self, n: usize) -> Result<&'b [u8], CodecError> {
        if self.finished() && n > 0 {
            return Err(CodecError::EndOfInput);
        }

        if self.bytes_left() >= n {
            let out = &self.buf[self.at..self.at + n];
            self.at += n;
            Ok(out)
        } else {
            Err(CodecError::EndOfInput)
        }
    }
}

/// Extension trait to decode more common structures.
pub trait DecoderExt<'de> {
    /// Takes a byte.
    fn take_u8(&mut self) -> Result<u8, CodecError>;

    /// Takes a varint.
    fn take_varint(&mut self) -> Result<Varint, CodecError>;

    /// Takes a varint-sized buf.
    fn take_var_buf(&mut self) -> Result<&'de [u8], CodecError>;
}

impl<'de, T: Decoder<'de>> DecoderExt<'de> for T {
    fn take_u8(&mut self) -> Result<u8, CodecError> {
        let b = self.take_next(1)?;
        Ok(b[0])
    }

    fn take_varint(&mut self) -> Result<Varint, CodecError> {
        Varint::decode(self)
    }

    fn take_var_buf(&mut self) -> Result<&'de [u8], CodecError> {
        let vi = self.take_varint()?;
        self.take_next(vi.to_u32() as usize)
    }
}

/// Common marshalling type for bytes on disk or over the wire in frames.
pub trait Codec: Encode + Decode {}

/// Blanket impl for owned 1:1 encodable/decodable types.
impl<T: Encode + Decode> Codec for T {}

/// Common marshalling type for storing a type as bytes.
pub trait Encode {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError>;
}

/// Common marshalling type for extracting a type from bytes.
pub trait Decode: Sized {
    fn decode<'de, D: Decoder<'de>>(dec: &mut D) -> Result<Self, CodecError>;
}

/// Common marshalling type for extracting a type from bytes, but enabling
/// unowned zero-copy decoding.
///
/// The `'c` lifetime is short for "context", as in the decoder context.
pub trait DecodeRef<'de>: Sized {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c;
}

/// Shim impl for when the type owns itself.
///
///
impl<T> Decode for T
where
    T: for<'de> DecodeRef<'de>,
{
    fn decode<'de, D: Decoder<'de>>(dec: &mut D) -> Result<Self, CodecError> {
        T::decode_ref(dec)
    }
}

impl Encode for () {
    fn encode<E: Encoder>(&self, _enc: &mut E) -> Result<(), CodecError> {
        Ok(())
    }
}

impl<'de> DecodeRef<'de> for () {
    fn decode_ref<'c, D: Decoder<'de>>(_dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        Ok(())
    }
}

/// Replicated behavior from § 2.1.1.
impl<T: Encode> Encode for Option<T> {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        match self {
            Some(v) => {
                enc.push_u8(0)?;
                v.encode(enc)
            }
            None => enc.push_u8(0),
        }
    }
}

/// Replicated behavior from § 2.1.1.
impl<'de, T: DecodeRef<'de>> DecodeRef<'de> for Option<T> {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        match dec.take_u8()? {
            0 => Ok(None),
            1 => Ok(Some(T::decode_ref(dec)?)),
            t => Err(CodecError::InvalidOptionTag(t)),
        }
    }
}

impl<T1: Encode, T2: Encode> Encode for Result<T1, T2> {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        match self {
            Ok(v) => {
                enc.push_u8(0)?;
                v.encode(enc)
            }
            Err(e) => {
                enc.push_u8(1)?;
                e.encode(enc)
            }
        }
    }
}

impl<'de, T1: DecodeRef<'de>, T2: DecodeRef<'de>> DecodeRef<'de> for Result<T1, T2> {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        match dec.take_u8()? {
            0 => Ok(Ok(T1::decode_ref(dec)?)),
            1 => Ok(Err(T2::decode_ref(dec)?)),
            t => Err(CodecError::InvalidOptionTag(t)),
        }
    }
}

impl Encode for Vec<u8> {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        enc.push_var_buf(&self)
    }
}

impl<'de> DecodeRef<'de> for Vec<u8> {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        dec.take_var_buf().map(|v| v.to_vec())
    }
}

impl<'b> Encode for &'b [u8] {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        enc.push_next(self)?;
        Ok(())
    }
}

impl<'de> DecodeRef<'de> for &'de [u8] {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        Ok(dec.take_var_buf()?)
    }
}

impl Encode for String {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        if self.len() > varint::MAX {
            return Err(CodecError::OversizeBuf(self.len()));
        }

        enc.push_var_buf(self.as_bytes())?;
        Ok(())
    }
}

impl<'de> DecodeRef<'de> for String {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        let buf = dec.take_var_buf()?;
        let s = std::str::from_utf8(buf).map_err(|_| CodecError::InvalidUtf8)?;
        Ok(s.to_owned())
    }
}

impl<'s> Encode for &'s str {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        if self.len() > varint::MAX {
            return Err(CodecError::OversizeBuf(self.len()));
        }

        enc.push_var_buf(self.as_bytes())?;
        Ok(())
    }
}

impl<'de> DecodeRef<'de> for &'de str {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        let buf = dec.take_var_buf()?;
        let s = std::str::from_utf8(buf).map_err(|_| CodecError::InvalidUtf8)?;
        Ok(s)
    }
}

impl<const N: usize> Encode for [u8; N] {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        enc.push_next(self)
    }
}

impl<'de, const N: usize> DecodeRef<'de> for [u8; N] {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        let buf = dec.take_next(N)?;
        assert_eq!(buf.len(), N);
        let mut dest = [0; N];
        dest.copy_from_slice(buf);
        Ok(dest)
    }
}

impl<'buf, const N: usize> Encode for &'buf [u8; N] {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        enc.push_next(*self)
    }
}

impl<'de, const N: usize> DecodeRef<'de> for &'de [u8; N] {
    fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
    where
        'de: 'c,
    {
        let buf = dec.take_next(N)?;
        assert_eq!(buf.len(), N);
        let arr = unsafe { mem::transmute::<_, &[u8; N]>(buf.as_ptr()) };
        Ok(arr)
    }
}

#[macro_export]
macro_rules! inst_wrapper_codec {
    ( $name:ty => $inner:ty ) => {
        impl Encode for $name {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                <$inner>::encode(&self.0, enc)?;
                Ok(())
            }
        }

        impl<'de> DecodeRef<'de> for $name {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                let inner = <$inner>::decode_ref(dec)?;
                Ok(Self(inner))
            }
        }
    };
}

macro_rules! inst_tuple_codec {
    ( $($n:ident: $t:ident),+ ) => {
        impl<$($t: Encode),+> Encode for ($($t),+) {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                let ($($n),+) = &self;
                $($n.encode(enc)?;)+
                Ok(())
            }
        }

        impl<'de, $($t: DecodeRef<'de>),+> DecodeRef<'de> for ($($t),+) {
            fn decode_ref<'c, DEC: Decoder<'de>>(dec: &'c mut DEC) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                $(let $n = $t::decode_ref(dec)?;)+
                    Ok(($($n),+))
            }
        }
    };
}

inst_tuple_codec!(a: A, b: B);
inst_tuple_codec!(a: A, b: B, c: C);
inst_tuple_codec!(a: A, b: B, c: C, d: D);

macro_rules! inst_integer_codec {
    ( $int:ty ) => {
        impl Encode for $int {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                let buf = <$int>::to_be_bytes(*self);
                enc.push_next(&buf)?;
                Ok(())
            }
        }

        impl<'de> DecodeRef<'de> for $int {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                const NBYTES: usize = (<$int>::BITS / 8) as usize;
                let mut arr = [0; NBYTES];
                let buf = dec.take_next(NBYTES)?;
                arr.copy_from_slice(buf);
                Ok(<$int>::from_be_bytes(arr))
            }
        }
    };
}

inst_integer_codec!(u8);
inst_integer_codec!(i8);
inst_integer_codec!(u16);
inst_integer_codec!(i16);
inst_integer_codec!(u32);
inst_integer_codec!(i32);
inst_integer_codec!(u64);
inst_integer_codec!(i64);

/// Instantiates encode and decode impls for flat arrays of a type of arbitrary
/// lengths.  Ideally this would be made universal but Rust doesn't support
/// specialization properly so support for array types in our hierarchy has to
/// be explicitly opt-in.
#[macro_export]
macro_rules! inst_array_codec {
    // For unowned types.
    ( $name:ident<$lt:lifetime> ) => {
        impl<$lt, const N: usize> Encode for [$name<$lt>; N] {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                for i in 0..N {
                    self[i].encode(enc)?;
                }
                Ok(())
            }
        }

        /// Use this with caution, I haven't bothered to make sure that the
        /// unsafe code here is right and I can't figure out how to get
        /// `MaybeUninit` to work to make it safer.
        impl<$lt, const N: usize> DecodeRef<$lt> for [$name<$lt>; N] {
            fn decode_ref<'c, D: Decoder<$lt>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                $lt: 'c,
            {
                let mut buf: [$name; N] = unsafe { mem::zeroed() };

                for i in 0..N {
                    match <$name>::decode_ref(dec) {
                        Ok(mut inst) => {
                            // Swap the entry in the buffer with the instance we
                            // just decoded, then drop the value we swapped out
                            // since it was never really initialized properly.
                            mem::swap(&mut inst, &mut buf[i]);
                            mem::forget(inst);
                        }
                        Err(e) => {
                            // Drop specifically the instances we really did
                            // decode already.
                            for e in &mut buf[..i] {
                                unsafe {
                                    ptr::drop_in_place(e as *mut $name);
                                }
                            }

                            // Now forget about the buffer since we explicitly
                            // dropped all the real entries.
                            mem::forget(buf);

                            return Err(e);
                        }
                    }
                }

                Ok(buf)
            }
        }
    };

    // For owned types.
    ( $name:ty ) => {
        impl<const N: usize> Encode for [$name; N] {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                for i in 0..N {
                    self[i].encode(enc)?;
                }
                Ok(())
            }
        }

        /// Use this with caution, I haven't bothered to make sure that the
        /// unsafe code here is right and I can't figure out how to get
        /// `MaybeUninit` to work to make it safer.
        impl<'de, const N: usize> DecodeRef<'de> for [$name; N] {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                let mut buf: [$name; N] = unsafe { mem::zeroed() };

                for i in 0..N {
                    match <$name>::decode_ref(dec) {
                        Ok(mut inst) => {
                            // Swap the entry in the buffer with the instance we
                            // just decoded, then drop the value we swapped out
                            // since it was never really initialized properly.
                            mem::swap(&mut inst, &mut buf[i]);
                            mem::forget(inst);
                        }
                        Err(e) => {
                            // Drop specifically the instances we really did
                            // decode already.
                            for e in &mut buf[..i] {
                                unsafe {
                                    ptr::drop_in_place(e as *mut $name);
                                }
                            }

                            // Now forget about the buffer since we explicitly
                            // dropped all the real entries.
                            mem::forget(buf);

                            return Err(e);
                        }
                    }
                }

                Ok(buf)
            }
        }
    };
}

/// Like [``inst_array_codec``], but for making a `Vec<_>` impl.
///
/// Does not support unowned types because if you think about that that kinda
/// doesn't make a lot of sense.
#[macro_export]
macro_rules! inst_vec_codec {
    ( $name:ty ) => {
        /// Mimics behavior from § 2.1.1, though does not have support for
        /// asserting smaller length bounds than varints naturally can hold.
        impl Encode for Vec<$name> {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                // Repeat this check early, since counts might be >4 Gi.
                if self.len() > $crate::varint::MAX as usize {
                    return Err(CodecError::InvalidVarintVal(self.len() as u64));
                }

                enc.push_varint($crate::varint::Varint::new(self.len() as u32)?)?;
                for e in self.iter() {
                    e.encode(enc)?;
                }

                Ok(())
            }
        }

        /// Mimics behavior from § 2.1.1, though does not have support for
        /// asserting smaller length bounds than varints naturally can hold.
        impl<'de> DecodeRef<'de> for Vec<$name> {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                let l = dec.take_varint()?;
                let mut buf = Vec::new();

                for _ in 0..l.to_u32() {
                    let v = <$name>::decode_ref(dec)?;
                    buf.push(v);
                }

                Ok(buf)
            }
        }
    };
}

/// Easy macro for creating codec impls for flat structs with simple codec
/// rules.  Expects a clean 1:1 conversion.  Does not support more complex
/// encoding schemes.
//
// TODO rewrite in terms of `inst_semimanual_codec`?
#[macro_export]
macro_rules! inst_struct_codec {
    // For unowned types.
    { $name:ident<$lt:lifetime> { $( $fname:ident : $fty:ty $(,)* )* } }  => {
        impl<$lt> Encode for $name<$lt> {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                $(
                    self.$fname.encode(enc)?;
                )*
                Ok(())
            }
        }

        impl<$lt> DecodeRef<$lt> for $name<$lt> {
            fn decode_ref<'c, D: Decoder<$lt>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                $lt: 'c,
            {
                let v = $name {
                    $(
                        $fname: <$fty>::decode_ref(dec)?,
                    )*
                };
                Ok(v)
            }
        }
    };

    // For owned types.
    { $name:ident { $( $fname:ident : $fty:ty $(,)* )* } }  => {
        impl Encode for $name {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                $(
                    Encode::encode(&self.$fname, enc)?;
                )*
                Ok(())
            }
        }

        impl<'de> DecodeRef<'de> for $name {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                let v = $name {
                    $(
                        $fname: <$fty>::decode_ref(dec)?,
                    )*
                };
                Ok(v)
            }
        }

    };

}

/// Internal macro for use in other type-level codec generators.
#[macro_export]
macro_rules! _int_gen_enc {
    (($outer:ident, $enc:ident) => ($fexpr:expr, field, $tyname:ty)) => {{
        let v = $fexpr;
        <$tyname as Encode>::encode(v, $enc)?;
    }};
    (($outer:ident, $enc:ident) => ($fexpr:expr, vec, $tyname:ty)) => {{
        let vec = $fexpr;
        let len_vi = $crate::varint::Varint::try_from(vec.len() as u64)?;
        $enc.push_varint(len_vi)?;
        for ent in vec {
            <$tyname as Encode>::encode(ent, $enc)?;
        }
    }};
    (($outer:ident, $enc:ident) => ($fexpr:expr, frombuf, $tyname:ty)) => {{
        let buf: &[u8] = ($fexpr).as_ref();
        $enc.push_var_buf(buf)?;
    }};
}

/// Internal macro for use in other type-level codec generators.
#[macro_export]
macro_rules! _int_gen_decref {
    (($outer:ident, $lt:lifetime, $dec:ident) => ($fname:ident, field, $tyname:ty)) => {
        <$tyname as DecodeRef<$lt>>::decode_ref($dec)?
    };
    (($outer:ident, $lt:lifetime, $dec:ident) => ($fname:ident, vec, $tyname:ty)) => {
        {
            let cnt = $dec.take_varint()?.to_u32() as usize;
            let mut buf = Vec::with_capacity(cnt);
            for i in 0..cnt {
                let inst: $tyname = <$tyname as DecodeRef<$lt>>::decode_ref($dec)?;
                buf.push(inst);
            }
            buf
        }
    };
    (($outer:ident, $lt:lifetime, $dec:ident) => ($fname:ident, frombuf, $tyname:ty)) => {
        {
            let buf = $dec.take_var_buf()?;
            <$tyname as TryFrom<&$lt [u8]>>::try_from(buf)
                .map_err(|_| CodecError::MissizeBuf(buf.len(), stringify!($outer)))?
        }
    };
}

#[macro_export]
macro_rules! inst_enum_codec {
    // For unowned types.
    { $name:ident<$lt:lifetime> $discrimty:ty { $( $vardiscrim:literal => $varname:ident ( $kind:ident $varty:ty ) $(,)* )* } } => {
        impl<$lt> Encode for $name<$lt> {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                match self {
                    $(
                        Self::$varname(vdata) => {
                            ($vardiscrim as $discrimty).encode(enc)?;
                            $crate::_int_gen_enc!(($name, enc) => (vdata, $kind, $varty));
                            Ok(())
                        }
                    ),*
                }
            }
        }

        impl<$lt> DecodeRef<$lt> for $name<$lt> {
            fn decode_ref<'c, D: Decoder<$lt>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                $lt: 'c,
            {
                let discrim = <$discrimty>::decode(dec)?;
                match discrim {
                    $(
                        $vardiscrim => {
                            let vdata = $crate::_int_gen_decref!(($name, $lt, dec) => (data, $kind, $varty));
                            Ok(Self::$varname(vdata))
                        }
                    ),*
                    _ => Err(CodecError::InvalidEnumVariant(stringify!($name), u64::from(discrim)))
                }
            }
        }
    };

    // For owned types.
    { $name:ident $discrimty:ty { $( $vardiscrim:literal => $varname:ident ( $kind:ident $varty:ty ) $(,)* )* } } => {
        impl Encode for $name {
            fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
                match self {
                    $(
                        Self::$varname(vdata) => {
                            ($vardiscrim as $discrimty).encode(enc)?;
                            $crate::_int_gen_enc!(($name, enc) => (vdata, $kind, $varty));
                            Ok(())
                        }
                    ),*
                }
            }
        }

        impl<'de> DecodeRef<'de> for $name {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                let discrim = <$discrimty>::decode_ref(dec)?;
                match discrim {
                    $(
                        $vardiscrim => {
                            let vdata = $crate::_int_gen_decref!(($name, 'de, dec) => (data, $kind, $varty));
                            Ok(Self::$varname(vdata))
                        }
                    ),*
                    _ => Err(CodecError::InvalidEnumVariant(stringify!($name), u64::from(discrim)))
                }
            }
        }
    };
}

/// Allows for more expressively describing certain kinds of struct types that
/// have variable size fields and weird conversions without having to stuble
/// over foreign types too badly.
#[macro_export]
macro_rules! inst_semimanual_codec {
    { $name:ident { $( $fname:ident $kind:ident $arg:ty $(,)* )* } } => {
        impl Encode for $name {
            fn encode<E: Encoder>(
                &self,
                enc: &mut E,
            ) -> Result<(), CodecError> {
                $(
                    {
                        $crate::_int_gen_enc!(($name, enc) => (&self.$fname, $kind, $arg));
                    }
                )*

                Ok(())
            }
        }

        impl<'de> DecodeRef<'de> for $name {
            fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
            where
                'de: 'c,
            {
                $(
                    let $fname = $crate::_int_gen_decref!(($name, 'de, dec) => ($fname, $kind, $arg));
                )*

                Ok(Self { $( $fname, )* })
            }
        }
    }
}

/// Instantiates codec impls for structs where all fields are already
/// `Codec`able.
#[macro_export]
macro_rules! inst_struct_codec_alt {
    { $name:ident { $( $fname:ident : $fty:ty $(,)* )* } } => {
        $crate::inst_semimanual_codec {
            $name { $( $fname field $fty, )* }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::*;

    #[derive(Clone, Debug, Eq, PartialEq)]
    struct Foo<'b> {
        v: u8,
        buf: &'b [u8],
    }

    impl<'b> Encode for Foo<'b> {
        fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
            enc.push_u8(self.v)?;
            enc.push_var_buf(self.buf)?;
            Ok(())
        }
    }

    impl<'de> DecodeRef<'de> for Foo<'de> {
        fn decode_ref<'c, D: Decoder<'de>>(dec: &'c mut D) -> Result<Self, CodecError>
        where
            'de: 'c,
        {
            let v = dec.take_u8()?;
            let buf = dec.take_var_buf()?;
            Ok(Self { v, buf })
        }
    }

    inst_array_codec!(Foo<'de>);

    #[test]
    fn test_struct_ref() {
        let buf = &[5, 3, 1, 2, 3];

        // This is in a block because we want to ensure that the lifetimes work out.
        let inst = { decode_borrowed::<Foo<'_>>(buf).expect("test: decode buf") };

        assert_eq!(inst.v, 5);
        assert_eq!(inst.buf, &[1, 2, 3]);

        let dup = encode_to_vec(&inst).expect("test: encode buf");

        assert_eq!(dup, buf);
    }

    #[test]
    fn test_array_struct_ref() {
        let buf = &[5, 3, 1, 2, 3, 7, 5, 10, 11, 12, 13, 14, 10, 2, 20, 21];

        let inst = { decode_borrowed::<[Foo<'_>; 3]>(buf).expect("test: decode buf") };

        assert_eq!(inst[0].v, 5);
        assert_eq!(inst[0].buf, &[1, 2, 3]);
        assert_eq!(inst[1].v, 7);
        assert_eq!(inst[1].buf, &[10, 11, 12, 13, 14]);
        assert_eq!(inst[2].v, 10);
        assert_eq!(inst[2].buf, &[20, 21]);
    }

    #[derive(Clone, Debug, Eq, PartialEq)]
    struct Bar([u8; 5]);

    inst_wrapper_codec!(Bar => [u8; 5]);

    #[test]
    fn test_wrapper_codec_impl() {
        let buf = &[5, 6, 7, 8, 9];
        let bar = Bar([5, 6, 7, 8, 9]);

        let inst = { decode::<Bar>(buf).expect("test: decode buf") };

        assert_eq!(inst, bar);
    }

    #[derive(Clone, Debug, Eq, PartialEq)]
    struct Baz {
        a: u8,
        b: u16,
        c: i32,
    }

    inst_struct_codec! {
        Baz {
            a: u8,
            b: u16,
            c: i32,
        }
    }

    struct Baz2<'buf> {
        v: i16,
        b: &'buf [u8],
    }

    inst_struct_codec! {
        Baz2<'buf> {
            v: i16,
            b: &[u8],
        }
    }

    #[derive(Debug, Eq, PartialEq)]
    enum Quux {
        Qa(u64),
        Qb(i32),
    }

    inst_enum_codec! {
        Quux u8 {
            2 => Qa(field u64),
            5 => Qb(field i32)
        }
    }

    #[derive(Debug, Eq, PartialEq)]
    enum Quux2<'buf> {
        Qa(u64),
        Qb(i32),
        Qc(&'buf [u8]),
    }

    inst_enum_codec! {
        Quux2<'buf> u8 {
            2 => Qa(field u64),
            5 => Qb(field i32),
            7 => Qc(field &[u8])
        }
    }

    #[test]
    fn test_enum_codec_impl_ok() {
        let buf = &[5, 255, 255, 255, 255];

        let inst = { decode::<Quux>(buf).expect("test: decode buf") };

        let exp = Quux::Qb(-1);
        assert_eq!(inst, exp);
    }

    #[test]
    fn test_enum_codec_impl_unknown() {
        let buf = &[1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

        let e = { decode::<Quux>(buf).expect_err("test: decode buf ok") };

        eprintln!("{:?}", e);
    }

    #[derive(Debug, Eq, PartialEq)]
    struct Frob {
        v: u32,
        w: [u8; 4],
    }

    inst_semimanual_codec! {
        Frob {
            v field u32,
            w frombuf [u8; 4]
        }
    }
}
